import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.List;
import java.util.Map;

import com.speljohan.rsbot.bot.Bot;
import com.speljohan.rsbot.event.events.ServerMessageEvent;
import com.speljohan.rsbot.event.listeners.PaintListener;
import com.speljohan.rsbot.event.listeners.ServerMessageListener;
import com.speljohan.rsbot.script.GEItemInfo;
import com.speljohan.rsbot.script.Script;
import com.speljohan.rsbot.script.wrappers.RSNPC;
import com.speljohan.rsbot.script.wrappers.RSTile;

/**
 * GMonkFisher.java
 * 
 * Fast and stable monk fishing script made by Gnarly for RSBot
 * 
 * @author Gnarly
 */
public class GMonkFisher extends Script implements PaintListener,
		ServerMessageListener {

	private static final DecimalFormat format = new DecimalFormat("00");
	
	private static final int SMALL_FISHING_NET = 303;
	private static final int RAW_MONKFISH = 7944;
	private static final int FISHING_SPOT = 3848;
	private static final int ARNOLD_LYDSPOR = 3824;
	private static final int FISHING_ANIMATION = 621;

	private RSTile currentFishingTile;
	private RSNPC currentFishingTileNPC;

	private long startTime;
	private long startingFishingExp;
	private long startingFishingLevel;

	private int totalCatches;
	private int rawMonkfishPrice;
	private int paintBoxAlpha;
	private int randomRun;

	private boolean scriptIsRunning;
	private boolean paintBoxAlphaState;
	private boolean firstTick;

	@Override
	public String getAuthor() {
		return "Gnarly";
	}

	@Override
	public String getName() {
		return "GMonkFisher";
	}

	@Override
	public double getVersion() {
		return 1.2;
	}

	@Override
	public String getScriptCategory() {
		return "Fishing";
	}

	@Override
	public String getScriptDescription() {
		String s = "";
		s += "<html>\n";
		s += "<body>\n";
		s += "<div style=\"width: 100%; height: 35px; background-color: #00CC33; text-align: center;\"\n";
		s += "<h2 style=\"color: #FFFFFF;\">" + getName() + "</h2>\n";
		s += "<div style=\"width: 100%; background-color: #008A22; text-align:center; color: #FFFFFF; height: 15px;\">" + getAuthor() + " | Version " + getVersion() + "</div>\n";
		s += "</div>\n";
		s += "<div align=\"center\">\n";
		s += "<p>Very fast and stable monk fishing script, this script is nearly flawless and can run for hours non-stop without messing up, unless a random comes and you have no anti-random for it. So make sure you have all the most-recently updated anti-randoms so that you're safe as possible while autoing!</p>\n";
		s += "<p>This script has a built in Anti-Ban on it, and a rather good one. If you have a Anti-Ban script disable it! Or it will make this script run slower because there will be two Anti-Bans running at the same time, and so that means lots of things will be happening, therefore slowing down the script! Also if you use the built-in Anti-ban, it checks to make sure that it's a good time to do the Anti-Ban, and it wont mess anything up or slow down the script!</p>\n";
		s += "<p>Start in piscatoris bank, or near a fishing spot. Make sure your inventory is complety empty except for one small fishing net before starting!</p>\n";
		s += "<div>\n";
		s += "</body>\n";
		s += "</html>\n";
		return s;
	}

	@Override
	public boolean onStart(final Map<String, String> map) {
		startTime = System.currentTimeMillis();
		log.info("Getting the market price for raw monkfishes.");
		final GEItemInfo rawMonkfish = grandExchange.loadItemInfo(RAW_MONKFISH);
		rawMonkfishPrice = rawMonkfish.getMarketPrice();
		log.info("Done getting market prices!");
		log.info("	-Raw Monkfish: " + rawMonkfishPrice + "gp");
		log.info("Done loading, initiating script...");
		paintBoxAlpha = 50;
		randomRun = random(50, 100);
		scriptIsRunning = true;
		firstTick = true;
		return true;
	}

	@Override
	public void onFinish() {
		Bot.getEventManager().removeListener(PaintListener.class, this);
		Bot.getEventManager().removeListener(ServerMessageListener.class, this);
		scriptIsRunning = false;
	}

	@Override
	public int loop() {
		if (scriptIsRunning && isLoggedIn() && firstTick) {
			startingFishingExp = skills.getCurrentSkillExp(STAT_FISHING);
			startingFishingLevel = skills.getCurrentSkillLevel(STAT_FISHING);
			firstTick = false;
		}
		if (getEnergy() > randomRun) {
			setRun(true);
			randomRun = random(50, 100);
		}
		if (isInventoryFull()) {
			if (bank.getInterface().isValid()) {
				bank.depositAllExcept(SMALL_FISHING_NET);
				bank.close();
			} else {
				if (!getMyPlayer().isMoving()) {
					final RSNPC arnold = getNearestNPCByID(ARNOLD_LYDSPOR);
					if (!(arnold == null)) {
						clickOnNPC(arnold, "bank");
					} else {
						walkTile(new RSTile(2330, 3689));
					}
				}
			}
		} else {
			if (waitToFish(random(2000, 2500))) {
				if (fishingSpotMoved()) {
					findFishingSpot();
				}
				if (random(0, 50) == 5 && !fishingSpotMoved()) {
					antiBan();
				}
			} else {
				findFishingSpot();
			}
		}
		return random(500, 800);
	}

	@Override
	public void onRepaint(final Graphics render) {
		if (!scriptIsRunning || !isLoggedIn())
			return;
		final Graphics2D g = (Graphics2D) render;
		g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
				RenderingHints.VALUE_ANTIALIAS_ON);
		g.setColor(new Color(0, 0, 0, 150));
		g.setColor(new Color(0, 255, 0, paintBoxAlpha));
		g.fillRoundRect(360, 8, 150, 100, 8, 8);
		g.setColor(new Color(255, 255, 255, paintBoxAlpha / 4));
		g.fillRoundRect(361, 9, 149, 50, 8, 8);
		if (paintBoxAlpha == 50) {
			paintBoxAlphaState = true;
		} else if (paintBoxAlpha == 100) {
			paintBoxAlphaState = false;
		}
		if (paintBoxAlphaState) {
			paintBoxAlpha++;
		} else {
			paintBoxAlpha--;
		}
		g.setColor(new Color(0, 0, 0, 125));
		g.fillRect(4, 124, 16, 100);
		g.setColor(new Color(0, 0, 0));
		g.drawRect(4, 124, 16, 100);
		g.setColor(new Color(0, 0, 0, 50));
		g.drawRect(5, 125, 14, 98);
		g.setColor(new Color(17, 255, 0, paintBoxAlpha / 2));
		g.fillRect(6, 126 + 96 - (int) (96 * (skills.getPercentToNextLevel(STAT_FISHING)) / 100.0), 12, (int) (96 * (skills.getPercentToNextLevel(STAT_FISHING)) / 100.0));
		g.setColor(new Color(0, 0, 0));
		g.drawRect(6, 126 + 96 - (int) (96 * (skills.getPercentToNextLevel(STAT_FISHING)) / 100.0), 12, (int) (96 * (skills.getPercentToNextLevel(STAT_FISHING)) / 100.0));
		g.setColor(new Color(255, 255, 255, paintBoxAlpha / 4));
		g.fillRect(4, 124, 16, 50);
		g.setColor(new Color(0, 0, 0, 25));
		g.fillRect(4, 224, 30, 16);
		g.setColor(new Color(0, 0, 0));
		g.drawRect(4, 224, 30, 16);
		g.setColor(new Color(0, 0, 0, 10));
		g.drawRect(5, 225, 28, 14);
		g.setColor(new Color(255, 255, 255));
		g.drawString(skills.getPercentToNextLevel(STAT_FISHING) + "%", 8, 237);
		g.setColor(new Color(255, 255, 255, 65));
		g.fillRect(4, 224, 30, 8);
		int paintItemX = 20;
		g.setColor(new Color(255, 255, 255, 150));
		final String[] paintItem = { getName() + " v" + getVersion(), "Time: " + timeSince(startTime),
				"Exp Gained: " + NumberFormat.getInstance().format(skills.getCurrentSkillExp(STAT_FISHING) - startingFishingExp),
				"Levels Gained: " + (skills.getCurrentSkillLevel(STAT_FISHING) - startingFishingLevel),
				"Exp to next lvl: " + NumberFormat.getInstance().format(skills.getXPToNextLevel(STAT_FISHING)),
				"Total Catches: " + NumberFormat.getInstance().format(totalCatches), 
				"Catches to next lvl: " + NumberFormat.getInstance().format(skills.getXPToNextLevel(STAT_FISHING) / 120),
				"Money Made: " + NumberFormat.getInstance().format(totalCatches * rawMonkfishPrice)
		};
		for (final String item : paintItem) {
			g.drawString(item, 363, paintItemX);
			paintItemX += 12;
		}
	}

	@Override
	public void serverMessageRecieved(final ServerMessageEvent e) {
		if (!scriptIsRunning || !isLoggedIn())
			return;
		if (e.getMessage().equalsIgnoreCase("you catch a monkfish.")) {
			totalCatches++;
		}
	}

	@Override
	public int getMouseSpeed() {
		return 6;
	}

	private boolean waitToFish(long time) {
		long waitTime = System.currentTimeMillis() + time;
		boolean found = false;
		while (System.currentTimeMillis() < waitTime && !found){
			wait(15);
			found = getMyPlayer().getAnimation() == FISHING_ANIMATION;
		}
		return found;
	}

	private void findFishingSpot() {
		final RSNPC fishingSpot = getNearestNPCByID(FISHING_SPOT);
		if (!(fishingSpot == null)) {
			if (!isFishing()) {
				clickOnNPC(fishingSpot, "net");
				currentFishingTile = fishingSpot.getLocation();
				currentFishingTileNPC = fishingSpot;
			}
		} else {
			walkTile(new RSTile(2330, 3689));
		}
	}

	private boolean fishingSpotMoved() {
		if (currentFishingTile == null || currentFishingTileNPC == null)
			return true;
		return !(currentFishingTileNPC.getLocation().equals(currentFishingTile));
	}

	private boolean isFishing() {
		return getMyPlayer().getAnimation() == FISHING_ANIMATION;
	}

	private String timeSince(final long time) {
		final int seconds = (int) ((System.currentTimeMillis() - time) / 1000);
		final int minutes = (seconds / 60);
		final int hours = (minutes / 60);
		return format.format(hours % 24) + ":" + format.format(minutes % 60)
				+ ":" + format.format(seconds % 60);
	}

	private RSTile generateTilePath(RSTile tile) {
		if (tileOnMap(tile))
			return tile;
		RSTile newTile = new RSTile((getMyPlayer().getLocation().getX() + tile.getX()) / 2,
				(getMyPlayer().getLocation().getY() + tile.getY()) / 2);
		return tileOnMap(newTile) ? newTile : generateTilePath(newTile);
	}

	private void walkTile(RSTile tile) {
		if (!getMyPlayer().isMoving() && !(distanceBetween(getMyPlayer().getLocation(), tile) < 5)) {
			if (distanceTo(tile) < 16) {
				walkTileMM(randomizeTile(tile, 1, 1));
			} else {
				walkTileMM(generateTilePath(randomizeTile(tile, 1, 1)));
			}
			if (random(0, 10) == 5) {
				antiBan();
			}
		}
	}

	private boolean clickOnNPC(final RSNPC npc, final String action) {
		if (npc == null)
			return false;
		final RSTile tile = npc.getLocation();
		if (!tile.isValid())
			return false;
		try {
			Point screenLoc = npc.getScreenLocation();
			if (distanceTo(tile) > 6 || !pointOnScreen(screenLoc)) {
				turnToTile(tile, 30);
				if (random(0, 5) == 0) {
					setCameraAltitude(false);
				}
			}
			if (!pointOnScreen(screenLoc) && getMyPlayer().getInteracting() == null) {
				walkTile(tile);
				return pointOnScreen(screenLoc);
			}
			for (int i = 0; i < 12; i++) {
				screenLoc = npc.getScreenLocation();
				if (!npc.isValid() || !pointOnScreen(screenLoc))
					return false;
				moveMouse(screenLoc, 5, 5);
				if (getMenuItems().get(0).toLowerCase().contains(
						npc.getName().toLowerCase()))
					break;
				if (getMouseLocation().equals(screenLoc))
					break;
			}
			final List<String> menuItems = getMenuItems();
			if (menuItems.isEmpty())
				return false;
			for (int a = 0; a < menuItems.size(); a++) {
				if (menuItems.get(a).toLowerCase().contains(
						npc.getName().toLowerCase())) {
					if (menuItems.get(0).toLowerCase().contains(
							action.toLowerCase())) {
						clickMouse(true);
						return true;
					} else {
						clickMouse(false);
						return atMenu(action);
					}
				}
			}
		} catch (final Exception e) {
			e.printStackTrace();
		}
		return false;
	}

	private void antiBan() {
		switch (random(0, 5)) {
			case 1:
				setCameraRotation(random(random(0, 100), random(200, 360)));
				break;
			case 2:
				moveMouse(random(0, 764), random(0, 502));
				break;
			case 3:
				switch (random(0, 5)) {
					case 1:
						openTab(TAB_INVENTORY);
						break;
					case 2:
						openTab(TAB_STATS);
						if (random(0, 1) == 0) {
							moveMouse(660, 290, 47, 24);
						}
						break;
					case 3:
						openTab(TAB_FRIENDS);
						if (random(0, 1) == 0) {
							moveMouse(560, 230, 153, 191);
						}
						break;
				}
				break;
			case 4:
				setCameraRotation((int)(getCameraAngle() + (Math.random() * 50 > 25 ? 1:-1)*(30 + Math.random() * 90)));
				// TODO Add more random Anti-Ban factors!
				break;
		}
	}
	
}
